﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using LaunchPad.Core.Dialogs;

namespace LaunchPad.Core.Controls
{
    /// <summary>
    /// Interaction logic for NetworkControl.xaml
    /// </summary>
    public partial class NetworkControl : UserControl
    {
        private readonly List<NodeDialog> _nodeDialogs = new List<NodeDialog>();

        public NetworkControl()
        {
            InitializeComponent();
            Unloaded += NetworkControlUnloaded;
        }

        private void NetworkControlUnloaded(object sender, RoutedEventArgs e)
        {
            _nodeDialogs.ForEach(dialog => dialog.Close());
        }

        private void DiscoverButtonClick(object sender, RoutedEventArgs e)
        {
            if(!XBeeIO.Instance.IsAvailable)
            {
                if (!XBeeIO.Instance.TryFindXBee())
                {
                    MessageBox.Show("Sorry, no XBee controller found (or possibly inaccessible)");
                    return;
                }
            }

            try
            {
                XBeeIO.Instance.BeginNodeDiscovery();
            }
            catch (Exception exception)
            {
                MessageBox.Show(string.Format("Sorry, couldn't connect to network.  {0}", exception));
            }
        }

        private void NodeListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems != null)
            {
                foreach (Node node in e.AddedItems)
                {
                    node.IsSelected = true;
                }
            }

            if (e.RemovedItems != null)
            {
                foreach (Node node in e.RemovedItems)
                {
                    node.IsSelected = false;
                }
            }
        }

        private void NodeListViewMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var node = nodeListView.SelectedItem as Node;

            if (node != null)
            {
                NodeDialog dialog = _nodeDialogs.Find(item => item.DataContext == node);

                if (dialog == null)
                {
                    var parentWindow = Window.GetWindow(this);
                    dialog = new NodeDialog {DataContext = node, Title = string.Format("Node {0}", node.Name), Owner = parentWindow};
                    dialog.Closed += DialogClosed;
                    dialog.Show();
                    _nodeDialogs.Add(dialog);
                }
                else
                {
                    dialog.Focus();
                }
            }
        }

        private void DialogClosed(object sender, EventArgs e)
        {
            _nodeDialogs.Remove(sender as NodeDialog);
        }

        #region DnD

        private Point _mouseStartPoint;

        private void NodeListViewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                _mouseStartPoint = Mouse.GetPosition(null);
                e.Handled = true;
            }
        }

        private void NodeListViewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                var node = nodeListView.SelectedItem as Node;

                if (node != null)
                {
                    Point mousePos = e.GetPosition(null);
                    Vector diff = _mouseStartPoint - mousePos;

                    bool minDragMet =
                        Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
                        Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance;

                    if (e.LeftButton == MouseButtonState.Pressed && minDragMet)
                    {
                        // Do drag and drop asynchronously
                        Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                               (Action) delegate
                                                            {
                                                                var dragData = new DataObject("nodeFormat", node);
                                                                DragDrop.DoDragDrop(this, dragData, DragDropEffects.Move);
                                                            });
                    }
                }
            }
        }

        #endregion

        private void AddNodeButtonClick(object sender, RoutedEventArgs e)
        {
            var field = DataContext as Field;

            if(field != null)
            {
                var allNames = Enumerable.Range(0, int.MaxValue).Select(i => string.Format("Node{0}", i));
                var existingNames = field.Nodes.Select(n => n.Name);
                var unusedNames = allNames.Except(existingNames);

                var name = unusedNames.First();
                field.Nodes.Add(new Node { Name = name });
            }
        }
    }
}